package net.minecraft.src;

import java.util.ArrayList;
import java.util.HashMap;

public class TeleportManager {
	
	private static ArrayList<TeleportNode> registeredLinks = new ArrayList<TeleportNode>();
	private static ArrayList<TeleportNode> registeredReceivers = new ArrayList<TeleportNode>();
	private static HashMap<String, TeleportNode> linkedSenders = new HashMap<String, TeleportNode>();
	private static HashMap<String, TeleportNode> unlinkedSenders = new HashMap<String, TeleportNode>();
	private static HashMap<String, TeleportNode> receiverList = new HashMap<String, TeleportNode>();
	
	private static TeleportNode registeredMarker = null;
	
	private static int respawnClickAmount = 0;
	private static int timesTeleported = 0;
	private static boolean hasTeleportSickness = false;
	private static long sicknessStartTime = 0;
	
	public static boolean doTeleport(EntityPlayer ep, TeleportNode sender){
		if(!idIsInvalid(sender) && !isLinked(sender) && !isUnlinked(sender))addNodeSender(ep, sender);
		else if(isUnlinked(sender)) findAndLink(unlinkedSenders.get(sender.getID()));
		if(isLinked(sender) && isRegisteredLink(sender)) teleport(ep, linkedSenders.get(sender.getID()));
		else if(isLinked(sender) && !isRegisteredLink(sender)){
			registeredLinks.add(sender);
			ep.addChatMessage("Teleporter linked!");
		}
		else { 
			ep.addChatMessage("Teleporter is not linked.");
			return false;
		}
		return true;
	}
	
	public static boolean registerNode(EntityPlayer ep, TeleportNode receiver){
		if(!idIsInvalid(receiver) && !isRegistered(receiver)) addNodeReceiver(ep, receiver);
		else {
			ep.addChatMessage("Teleporter is already registered.");
			return false;
		}
		return true;
	}
	
	public static boolean registerMarker(EntityPlayer ep, TeleportNode marker){
		registeredMarker = marker;
		ep.addChatMessage("Marker set!");
		return true;
	}
	
	public static boolean useRecall(EntityPlayer ep){
		if(registeredMarker == null){
			ep.addChatMessage("No markers exist.");
			return false;
		}
		if(isSick(ep)) return false;
		updateTimesTeleported(ep, 1);
		ep.addChatMessage("Recalling...");
		double posX = registeredMarker.getXcoord() + 0.5;
		double posY = registeredMarker.getYcoord();
		double posZ = registeredMarker.getZcoord() + 0.5;
		ep.setLocationAndAngles(posX, posY, posZ, ep.rotationYaw, ep.rotationPitch);
		registeredMarker.removeID();
		registeredMarker = null;
		return true;
	}
	
	public static boolean doRespawn(EntityPlayer ep){
		if(isSick(ep)) return false;
		if(respawnClickAmount == 1){
			updateTimesTeleported(ep, 2);
			ep.respawnPlayer();
			ep.addChatMessage("You respawn with a pounding headache and no items.");
			respawnClickAmount = 0;
			return true;
		}
		ep.addChatMessage("Warning! If you click the home inscription a second time, you will respawn and you will LOSE ALL OF YOUR ITEMS!");
		respawnClickAmount++;
		return false;
	}
	
	public static void teleport(EntityPlayer ep, TeleportNode node){
		if(isSick(ep)) return;
		updateTimesTeleported(ep, 0);
		ep.addChatMessage("Teleporting...");
		TeleportNode valueNode = getLink(node);
		double posX = valueNode.getXcoord() + 0.5;
		double posY = valueNode.getYcoord();
		double posZ = valueNode.getZcoord() + 0.5;
		ep.setLocationAndAngles(posX, posY, posZ, ep.rotationYaw, ep.rotationPitch);
	}
	
	public static void addNodeSender(EntityPlayer ep, TeleportNode node){
		unlinkedSenders.put(node.getID(), node);
		findAndLink(node);
	}
	
	public static void addNodeReceiver(EntityPlayer ep, TeleportNode node){
		receiverList.put(node.getID(), node);
		registeredReceivers.add(node);
		ep.addChatMessage("Teleporter registered!");
	}
	
	public static void removeNode(TeleportNode node){
		String nodeID = node.getID();
		if(node.isSender()){
			System.out.println("Removing a sender...");
			if(linkedSenders.containsKey(nodeID)){
				if(isRegisteredLink(node)){
					linkedSenders.remove(nodeID);
					removeRegisteredLink(node);
					unlinkedSenders.put(nodeID, node);
				}
				else {
					linkedSenders.remove(nodeID);
					unlinkedSenders.put(nodeID, node);
				}
			}
			else if(unlinkedSenders.containsKey(nodeID)){
				unlinkedSenders.remove(nodeID);
				node.removeID();
			}
		}
		else if(receiverList.containsKey(nodeID)){
			System.out.println("Removing a receiver...");
			receiverList.remove(nodeID);
			registeredReceivers.remove(node);
			node.removeID();
		}
	}
	
	public static boolean idIsInvalid(TeleportNode node){
		return node.getID().equals("invalid");
	}
	
	public static boolean isLinked(TeleportNode sender){
		return linkedSenders.containsKey(sender.getID());
	}
	
	public static boolean isUnlinked(TeleportNode sender){
		return unlinkedSenders.containsKey(sender.getID());
	}
	
	public static boolean isRegistered(TeleportNode receiver){
		return receiverList.containsKey(receiver.getID());
	}
	
	public static boolean isRegisteredLink(TeleportNode sender){
		for(int listPos = 0; listPos < registeredLinks.size(); listPos++){
			if(registeredLinks.get(listPos).compareTo(sender)) return true;
		}
		return false;
	}
	
	public static void removeRegisteredLink(TeleportNode sender){
		for(int listPos = 0; listPos < registeredLinks.size(); listPos++){
			if(isSameNode(registeredLinks.get(listPos), sender)){
				registeredLinks.remove(listPos);
			}
		}
	}
	
	public static boolean isSick(EntityPlayer ep){
		if(hasTeleportSickness){
			ep.damageEntity(4);
			ep.getHurtSound();
			ep.addChatMessage("You have teleporter sickness! You'll have to wait until you feel better to use this inscription.");
		}
		return hasTeleportSickness;
	}
	
	public static TeleportNode getLink(TeleportNode sender){
		return receiverList.get(sender.getID());
	}
	
	public static boolean hasSameID(TeleportNode sender, TeleportNode receiver){
		return sender.getID().equals(receiver.getID());
	}
	
	public static boolean isSameNode(TeleportNode sender1, TeleportNode sender2){
		return sender1.compareTo(sender2);
	}
	
	public static void onTick(World world, EntityPlayer ep){
		if(!registeredLinks.isEmpty()){
			TeleportNode tempNode;
			for(int listPos = 0; listPos < registeredLinks.size(); listPos++){
				tempNode = registeredLinks.get(listPos);
				if(isBroken(world, tempNode)){
					removeNode(tempNode);
					ep.addChatMessage("Sender node is broken!");
				}
			}
		}
		if(!registeredReceivers.isEmpty()){
			TeleportNode tempNode;
			for(int listPos = 0; listPos < registeredReceivers.size(); listPos++){
				tempNode = registeredReceivers.get(listPos);
				if(isBroken(world, tempNode)){
					removeNode(tempNode);
					ep.addChatMessage("Receiver node is broken!");
				}
			}
		}
		checkForNeededUnlinks();
	}
	
	private static void updateTimesTeleported(EntityPlayer ep, int teleportType){
		if(teleportType == 0) timesTeleported += 1; //regular teleport
		else if(teleportType == 1) timesTeleported += 5; //recall teleport
		else if(teleportType == 2) timesTeleported += 20; //respawn teleport
		
		if(timesTeleported >= 20){
			hasTeleportSickness = true;
			sicknessStartTime = System.nanoTime();
			ep.addChatMessage("Your body becomes weak due to teleport sickness.");
		}
	}
	
	private static boolean isBroken(World world, TeleportNode node){
		if(node == null) return false;
		int nodeX = node.getXcoord();
		int nodeY = node.getYcoord();
		int nodeZ = node.getZcoord();
		boolean isSender = node.isSender();
		int[] worldSig = Inscription.readSignature(world, isSender, nodeX, nodeY, nodeZ);
		int[] nodeSig = node.getSignature();
		for(int pos = 0; pos < nodeSig.length; pos++){
			if(worldSig[pos] != nodeSig[pos]) return true;
		}
		return false;
	}
	
	private static boolean findAndLink(TeleportNode sender){
		String nodeID = sender.getID();
		if(receiverList.containsKey(nodeID)){
			unlinkedSenders.remove(nodeID);
			linkedSenders.put(nodeID, sender);
			return true;
		}
		return false;
	}
	
	private static void checkForNeededUnlinks(){
		TeleportNode tempNode;
		String nodeID;
		for(int pos = 0; pos < registeredLinks.size(); pos++){
			tempNode = registeredLinks.get(pos);
			nodeID = tempNode.getID();
			if(!receiverList.containsKey(nodeID)){
				unlinkedSenders.put(nodeID, tempNode);
				linkedSenders.remove(nodeID);
				registeredLinks.remove(pos);
			}
		}
	}
	
	private static void timedUnlinkCacheClear(){
		unlinkedSenders.clear();
		System.out.println("Cleared unlinked cache.");
	}
	
	public static boolean onTime(EntityPlayer ep, double computedTime, double eventTime){
		boolean cacheCleared = false;
		if(computedTime % eventTime < 0.03){ //modded values must be less than 0.03!!
			timedUnlinkCacheClear();
			System.out.println("Time=" + computedTime);
			cacheCleared = true;
			//clear out times teleported every 10 minutes
			timesTeleported = 0;
		}
		if(hasTeleportSickness){
			double sickTimeComputed = (System.nanoTime() - sicknessStartTime) * 0.000000001;
			if(sickTimeComputed >= 30.0){
				hasTeleportSickness = false;
				timesTeleported = 0;
				ep.addChatMessage("Your sickness has passed!");
			}
		}
		return cacheCleared;
	}
}
